package com.bestv.bsr.core.service.impl.zt;

import cn.hutool.core.collection.CollectionUtil;
import com.bestv.bsr.core.constants.AiSourceConstants;
import com.bestv.bsr.core.constants.ServiceCodeEnum;
import com.bestv.bsr.core.context.RecommendContext;
import com.bestv.bsr.core.context.RecommendContextHolder;
import com.bestv.bsr.core.entities.vo.RecDataVo;
import com.bestv.bsr.core.entities.vo.RecExtInfosVo;
import com.bestv.bsr.core.entities.vo.RecGroupListVo;
import com.bestv.bsr.core.entities.vo.RecItemVoListVo;
import com.bestv.bsr.core.exceptions.ServiceException;
import com.bestv.bsr.core.properties.ZTRecommendProperties;
import com.bestv.bsr.core.service.AbstractRecommendEngine;
import com.bestv.bsr.core.service.impl.zt.dto.ZTRequestParam;
import com.bestv.bsr.core.service.impl.zt.dto.ZTResponseDto;
import com.bestv.bsr.core.service.impl.zt.remote.ZTRemoteService;
import com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author taojiacheng
 */
@Slf4j
public abstract class AbstractZTRecommendEngine extends AbstractRecommendEngine {

    @Autowired
    protected ZTRecommendProperties properties;
    @Autowired
    protected ZTRemoteService ztRemoteService;

    private static final String ALL = "all";

    @Override
    public void init(RecommendContext recommendContext) {
        String sceneId = recommendContext.getSceneId();
        // 点播
        String sceneStr = properties.getSceneMap().get(sceneId);
        if (StringUtils.isNotBlank(sceneStr)) {
            recommendContext.setSceneType("0");
            recommendContext.setThirdSceneId(sceneStr);
            return;
        }
        // 直播
        sceneStr = properties.getLiveSceneMap().get(sceneId);
        if (StringUtils.isNotBlank(sceneStr)) {
            recommendContext.setSceneType("1");
            recommendContext.setThirdSceneId(sceneStr);
            return;
        }
        // 回看
        sceneStr = properties.getLookbackSceneMap().get(sceneId);
        if (StringUtils.isNotBlank(sceneStr)) {
            recommendContext.setSceneType("2");
            recommendContext.setThirdSceneId(sceneStr);
            return;
        }
        log.error("sceneId {} not supported", sceneId);
        throw new ServiceException(ServiceCodeEnum.SCENE_ID_NOT_SUPPORTED);
    }

    /**
     * 转换中台返回的数据
     *
     * @param responseDto responseDto
     * @return RecDataVo
     */
    protected RecDataVo convertResponse(ZTResponseDto responseDto) {
        RecDataVo recDataVo = new RecDataVo();
        if (responseDto == null) {
            return recDataVo;
        }
        RecommendContext recommendContext = RecommendContextHolder.getRecommendContext();
        List<String> itemCodes = recommendContext.getItemCodes();
        recDataVo.setExpId(responseDto.getExpId());
        recDataVo.setStrategyId(responseDto.getStrategyId());
        recDataVo.setRecResultId(responseDto.getLogId());
        List<ZTResponseDto.ZTRecommendItem> dataList = responseDto.getData();
        if (CollectionUtil.isNotEmpty(dataList)) {
            Map<String, RecGroupListVo> groupVoMap = new HashMap<>();
            List<RecExtInfosVo> extInfoVos = new ArrayList<>();
            for (ZTResponseDto.ZTRecommendItem data : dataList) {
                if (!CollectionUtils.isEmpty(itemCodes) && itemCodes.contains(data.getItemCode())) {
                    continue;
                }
                String itemType = data.getItemType();
                RecGroupListVo recGroupListVo = groupVoMap.computeIfAbsent(itemType, k -> new RecGroupListVo(k, new ArrayList<>()));
                recGroupListVo.getItemCodeList().add(data.getItemCode());

                RecItemVoListVo recItemVoListVo = new RecItemVoListVo();
                recItemVoListVo.setItemCode(data.getItemCode());
                recItemVoListVo.setItemName(data.getItemName());
                recItemVoListVo.setMedMarkPos(data.getMedMarkPos());
                recItemVoListVo.setPoster(data.getPoster());
                recItemVoListVo.setApkurl(data.getApkurl());
                recGroupListVo.getRecItemVoList().add(recItemVoListVo);
                extInfoVos.add(new RecExtInfosVo(data.getItemCode(), data.getRetrieveId(), recommendContext.getSceneType()));
            }

            recDataVo.setGroupList(new ArrayList<>(groupVoMap.values()));
            recDataVo.setExtInfos(extInfoVos);
        }

        return recDataVo;
    }

    /**
     * 根据终端请求的节目类型对中台返回的数据进行处理
     *
     * @param itemTypes   itemTypes
     * @param responseDto responseDto
     * @return RecDataVo
     */
    protected ZTResponseDto handlerZTResponse(Set<String> itemTypes, ZTResponseDto responseDto) {
        RecommendContext recommendContext = RecommendContextHolder.getRecommendContext();
        int size = recommendContext.getSize();
        List<ZTResponseDto.ZTRecommendItem> respData = responseDto.getData();
        Stream<ZTResponseDto.ZTRecommendItem> stream = respData.stream();
        // 包含all 不过滤，否则滤除不需要的itemType
        if (!itemTypes.contains(ALL)) {
            stream = stream.filter(item -> itemTypes.contains(item.getItemType()));
        }
        // 结果按照itemType分组
        Map<String, List<ZTResponseDto.ZTRecommendItem>> groupMap =
                stream.collect(Collectors.groupingBy(ZTResponseDto.ZTRecommendItem::getItemType));
        // 按照 value 的size 由小到大排序
        List<Map.Entry<String, List<ZTResponseDto.ZTRecommendItem>>> sortedList = groupMap.entrySet().stream()
                .sorted(Comparator.comparingInt(o -> o.getValue().size()))
                .collect(Collectors.toList());
        int subSize = size / groupMap.size();
        int modSize = size % groupMap.size();
        int currSubSize = subSize + modSize;
        List<ZTResponseDto.ZTRecommendItem> result = new ArrayList<>();
        for (int i = 0; i < sortedList.size(); i++) {
            Map.Entry<String, List<ZTResponseDto.ZTRecommendItem>> entry = sortedList.get(i);
            List<ZTResponseDto.ZTRecommendItem> dataList = entry.getValue();
            int tempSubSize = Math.min(currSubSize, dataList.size());
            if (tempSubSize > 0) {
                result.addAll(dataList.subList(0, tempSubSize));
            }
            // 当前类型数量不足，剩余需要补上的数量
            int leftSize = currSubSize - tempSubSize;
            // 剩余需要分配的类型数
            int leftTypes = dataList.size() - i - 1;
            if (leftSize > 0 && leftTypes > 0) {
                // 剩余需要补上的数量平均分配到其它未分配的类型上
                subSize += leftSize / leftTypes;
                modSize = leftSize % leftTypes;
                currSubSize = subSize + modSize;
            } else {
                currSubSize = subSize;
            }
        }
        responseDto.setData(result);
        return responseDto;
    }


    /**
     * 构建请求中台的参数
     *
     * @return ZTRequestParam
     */
    protected ZTRequestParam buildZtRequestParams(RecommendContext recommendContext) {
        String station = StringUtils.isNotBlank(recommendContext.getStation()) ? recommendContext.getStation() : properties.getBizField();
        ZTRequestParam ztRequestParam = ZTRequestParam.builder()
                .userId(recommendContext.getUserId())
                .size(recommendContext.getSize())
                .sceneId(recommendContext.getThirdSceneId())
                .itemTypes(Joiner.on(",").skipNulls().join(recommendContext.getItemTypes()))
                .site(station)
                .osProfile(recommendContext.getOsProfile())
                .userGroupCode(recommendContext.getUserGroup())
                .apkCode(recommendContext.getApkCode())
                .sn(recommendContext.getSn())
                .build();
        if (!CollectionUtils.isEmpty(recommendContext.getItemCodes())) {
            ztRequestParam.setItemCode(recommendContext.getItemCodes().get(0));
        }
        return ztRequestParam;
    }

    @Override
    protected List<String> addSupportedAiSource() {
        return Collections.singletonList(AiSourceConstants.AI_SOURCE_BESTV);
    }

}
